package info.niwota.web.apache;

import info.niwota.commons.acl.AccessControl;
import info.niwota.web.Debug;
import info.niwota.web.NVPair;
import info.niwota.web.Preferences;
import info.niwota.web.Session;
import info.niwota.web.SessionManager;
import info.niwota.web.user.UserManager;

import java.util.List;

import org.json.JSONObject;

import x.org.apache.http.HttpRequest;
import x.org.apache.http.HttpResponse;
import x.org.apache.http.protocol.HttpContext;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.text.TextUtils;
import android.util.Log;

/**
 * @author qiangli
 * 
 */
public class LoginHandler extends BaseHandler {

	private static final String TAG = "LoginHandler";

	public LoginHandler(Context ctx) {
		super(ctx);
	}

	@Override
	public boolean handle(String p, HttpRequest req,
			HttpResponse res, HttpContext context) throws Exception {
		// handle login, home pages
		if (Debug.DEBUG) {
			Log.d(TAG, "handle request: " + req);
		}

		if (Debug.DEBUG) {
			Log.d(TAG, "handle: " + p);
		}
		try {

			if (p != null && p.startsWith("/login")) {
				final String method = req.getRequestLine().getMethod();
				if (method.equals("GET")) {
					doGet(req, res, context);
				} else if (method.equals("POST")) {
					doPost(req, res, context);
				}
				return true;
			}
		} catch (Exception e) {
			e.printStackTrace();
			sendError(req, res, e);
		}
		return false;
	}

	protected void doGet(HttpRequest req, HttpResponse res, HttpContext context)
			throws Exception {
		final String[] data = new String[2];
		final List<NVPair> params = getParams(context);
		final String user = getUsername(params);
		data[0] = "";
		data[1] = user;
		doLogin(req, res, context, data);
	}

	private void doLogin(HttpRequest req, HttpResponse res, HttpContext context,
			final String[] data) throws Exception {
		try {
			String error = data[0];
			String user = data[1];
			//create a session to store nonce
			Session s = getSession(req, res, context, user, AccessControl.ACCESS_PUBLIC, false);
			String nonce= UserManager.salt();
			s.nonce = nonce;
			JSONObject obj= new JSONObject();
			obj.put("error", error);
			obj.put("username", user);
			obj.put("nonce", nonce);
			
			sendAssetData(req, res, "login.tpl", obj);
		} catch (Exception e) {
			throw e;
		}
	}

	private Session getSession(HttpRequest request,
			HttpResponse response, HttpContext context, String user, int group, boolean auth) {
		// get or create
		Session s = getSession(context);
		//
		if (s == null) {
			s = SessionManager.create();
		}
		s.user = user;
		s.group = group;
		s.authenticated = auth;
		//
		context.setAttribute(SESSION_KEY, s);
		return s;
	}

	protected void doPost(HttpRequest req, HttpResponse res, HttpContext context)
			throws Exception {
		try {
			// check login
			List<NVPair> params = getParams(context);
			final String user = getUsername(params);
			final String pass = getPassword(params);
			final String ts = getNonce(params);
			
			if (Debug.DEBUG) {
				Log.d(TAG, "doPost: " + user + ":" + pass +" nonce: " + ts);
			}
			//
			if (TextUtils.isEmpty(user) || TextUtils.isEmpty(pass)) {
				redirectLogin(req, res);
				return;
			}

			String[] data = new String[2];
			data[0] = "Invalid username/password or access disabled";
			data[1] = user;
			//
			final SharedPreferences p = PreferenceManager
					.getDefaultSharedPreferences(ctx);
			boolean isMasterEnabled = p.getBoolean(Preferences.ACCESS_MASTER,
					true);
			final String MASTER = "master";
			//
			String mauser = p.getString(Preferences.MASTER_USERNAME, MASTER);
			String mapwd = p.getString(Preferences.MASTER_PASSWORD, null);
			//
			boolean isMaster = user.equalsIgnoreCase(mauser);

			// check access
			if (isMaster) {
				Session s = getSession(context);
				String hash = digest(mapwd, s.nonce, ts);
				if (isMasterEnabled && hash.equals(pass)) {
					getSession(req, res, context, mauser, AccessControl.ACCESS_MASTER, true);
					redirectHome(req, res);
				} else {
					doLogin(req, res, context, data);
				}
				return;
			}
			//

			boolean isSharedEnabled = p.getBoolean(Preferences.ACCESS_SHARED,
					false);

			if (!isSharedEnabled) {
				doLogin(req, res, context, data);
				return;
			}
			//
			Session s = getSession(context);
			if (authenticate(user, pass, s.nonce, ts)) {
				getSession(req, res, context, user, AccessControl.ACCESS_SHARED, true);
				//
				redirectHome(req, res);
			} else {
				doLogin(req, res, context, data);
			}
		} catch (Exception e) {
			throw e;
		}
	}

	private String digest(String pass, String nonce, String ts)
			throws Exception {
		return UserManager.digest(pass +":" + nonce + ":" + ts);
	}

	private String getUsername(List<NVPair> params) {
		// username case insensitive
		Object user = getParam(params, "username", null);
		return (user == null ? "" : user.toString().toLowerCase());
	}

	private String getNonce(List<NVPair> params) {
		Object nonce = getParam(params, "nonce", null);
		return (nonce == null) ? "" : nonce.toString();
	}
	
	private String getPassword(List<NVPair> params) {
		Object pass = getParam(params, "password", null);
		return (pass == null) ? "" : pass.toString();
	}

	private boolean authenticate(String user, String pwd, String nonce, String ts) {
		if (Debug.DEBUG) {
			Log.d(TAG, "authenticate: " + user + " pass: " + pwd +" nonce: " + nonce +" ts: " + ts);
		}
		try {
			String pass = UserManager.getHash(ctx, user);
			String hash =  digest(pass, nonce, ts);
			if (Debug.DEBUG) {
				Log.d(TAG, "hash: " + hash + " stored pass: " + pass);
			}
			return (pwd.equals(hash));
		} catch (Exception e) {
			e.printStackTrace();
		}
		return false;
	}

}
